#include <jni.h>
#include <string>

extern "C"
{
#include  "librtmp/rtmp.h"
}

#include <android/log.h>

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,"RTMP推流查看 ",__VA_ARGS__)

//如何确定 要选择哪种数据类型            补充一下C++基础（慢慢来）  查看C++ Primer Plus
typedef struct {
    int8_t *sps;
    int16_t sps_len;            //长度是两个字节

    int16_t pps_len;
    int8_t *pps;

    RTMP *rtmp;
} Live;
Live *live = nullptr;

int sendVideo(jbyte *data, int len, int tms);

void prepareVideo(int8_t *buf, int len, Live *live);

int sendAudio(int8_t *buf, int len, int type, int tms);

RTMPPacket *createVideoPackage(Live *live);

int sendPacket(RTMPPacket *pPacket);

RTMPPacket *createVideoPackage(int8_t *buf, int len, const long tms, Live *live);

RTMPPacket *
createAudioPacket(int8_t *buf, const int len, const int type, const long tms, Live *live);

extern "C"
JNIEXPORT jboolean JNICALL
Java_it_jack_practicertmp_ScreenLive_connect(JNIEnv *env, jobject thiz, jstring _url) {

//    const char *url = env->GetStringUTFChars(url_, 0);
//    int ret;
//    do {
//        live = (Live*)malloc(sizeof(Live));
//        memset(live, 0, sizeof(Live));
//        live->rtmp = RTMP_Alloc();
//        RTMP_Init(live->rtmp);
//        live->rtmp->Link.timeout = 10;
//        LOGI("connect %s", url);
//        if (!(ret = RTMP_SetupURL(live->rtmp, (char*)url))) break;
//        RTMP_EnableWrite(live->rtmp);
//        LOGI("RTMP_Connect");
//        if (!(ret = RTMP_Connect(live->rtmp, 0))) break;
//        LOGI("RTMP_ConnectStream ");
//        if (!(ret = RTMP_ConnectStream(live->rtmp, 0))) break;
//        LOGI("connect success");
//    } while (0);
//    if (!ret && live) {
//        free(live);
//        live = nullptr;
//    }
//
//    env->ReleaseStringUTFChars(url_, url);
//    return ret;
    //将url转成c语言的字符串
    char *url = const_cast<char *>(env->GetStringUTFChars(_url, 0));

    int ret;

    do {
        //问：只有连接失败的情况下才会跳出循环，如果一直连接成功  岂不是一直在执行循环中的逻辑？
        live = (Live *) malloc(sizeof(Live));
        //调用初始化内存函数，定义变量时一定要进行初始化，尤其是数组和结构体这种占用内存大的数据结构
        memset(live, 0, sizeof(Live));
        //1.实例化rtmp对象：RTMP_Alloc，将实例化的rtmp需要赋值给live的rtmp
        live->rtmp = RTMP_Alloc();
        //2.初始化rtmp对象：RTMP_Init
        RTMP_Init(live->rtmp);
        //3.设置超时时间 Link.timeout
        live->rtmp->Link.timeout = 10;
        //4.设置URL地址：RTMP_SetupURL
        //如果返回的是1表示成功，否则表示失败   定义标记
        if (!(ret = RTMP_SetupURL(live->rtmp, url))) {
            break;
        }
        if (ret == TRUE) {
            LOGI("_SetupURL");
        }

        //5.开启输出模式:RTMP_EnableWrite,开启可写
        LOGI("_SetupURL 1");
        RTMP_EnableWrite(live->rtmp);
        LOGI("_SetupURL 2");

        //6.连接服务器
        //ret = RTMP_Connect(rtmp, 0);
        if (!(ret = RTMP_Connect(live->rtmp, 0))) {
            LOGI("_SetupURL 3 ");
            break;
        }
        LOGI("_SetupURL 4");
        if (ret == TRUE) {
            LOGI("_SetupURL 5");
            LOGI("_Connect");
        }

        //7.连接流
        //ret = RTMP_ConnectStream(rtmp, 0);
        if (!(ret = RTMP_ConnectStream(live->rtmp, 0))) {
            break;
        }
        if (ret == TRUE) {
            LOGI("connect success");
        }
    } while (0);

    if (!ret && live) {
        free(live);
        live = nullptr;
    }
    //需要释放处理
    env->ReleaseStringUTFChars(_url, url);
    return ret;
}

extern "C"
JNIEXPORT jboolean JNICALL
Java_it_jack_practicertmp_ScreenLive_sendData(JNIEnv *env, jobject thiz, jbyteArray data_, jint len,
                                              jlong tms, jint type) {
    int ret;//这个方法的返回值貌似可以不需要  待测试
    //把java的data_（byte数组）转成c的
    jbyte *data = env->GetByteArrayElements(data_, NULL);

    //根据类型进行区分，是发送视频包还是音频包
    switch (type) {
        case 0:
            ret = sendVideo(data, len, tms);
            break;
        default:
            ret = sendAudio(data, len, type, tms);
            break;
    }
    env->ReleaseByteArrayElements(data_, data, 0);
    return ret;
}

int sendAudio(int8_t *buf, int len, int type, int tms) {
    //创建音频包   如何组装音频包
    RTMPPacket *packet = createAudioPacket(buf, len, type, tms, live);
    int ret = sendPacket(packet);
    return ret;
}

RTMPPacket *
createAudioPacket(int8_t *buf, const int len, const int type, const long tms, Live *live) {
    //组装音频包
    int body_size = len + 2;   //长度 + 2
    RTMPPacket *packet = (RTMPPacket *) malloc(sizeof(RTMPPacket));
    RTMPPacket_Alloc(packet, body_size);
    packet->m_body[0] = 0xAF;
    //type == 1 表示是音频头
    if (type == 1) {
        packet->m_body[1] = 0x00;   //设置为0x00
    } else {
        packet->m_body[1] = 0x01;   //非音频头，设置为0x01
    }

    memcpy(&packet->m_body[2], buf, len);

    packet->m_packetType = RTMP_PACKET_TYPE_AUDIO;
    packet->m_nChannel = 0x05;//通道id，要跟视频设置的值不同
    packet->m_nBodySize = body_size;
    packet->m_nTimeStamp = tms;
    packet->m_hasAbsTimestamp = 0;
    packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
    packet->m_nInfoField2 = live->rtmp->m_stream_id;
    return packet;
}

int sendVideo(int8_t *buf, int len, int tms) {
    int ret = 0;
    int type = (buf[4] & 0x1F);
    if (type == 7) {
        //缓存sps和pps
        if (live && (!live->pps || !live->sps)) {
            //缓存
            prepareVideo(buf, len, live);
        }
    } else {
        //可能是连续发两帧
//        if (type == 5) {
//            //I帧 关键帧  拼接Packet
//            RTMPPacket *packet = createVideoPackage(live);
//            ret = sendPacket(packet);
//        } else {
//            //非关键帧 拼接Packet
//            RTMPPacket *packet = createVideoPackage(buf, len, tms, live);
//            ret = sendPacket(packet);
//        }
        //如果写成上方的方式 则无法推送就成功 具体原因是什么?

        if (type == 5) {//关键帧
            //I帧  拼接Packet
            RTMPPacket *packet = createVideoPackage(live);
            sendPacket(packet);
        }
        //非关键帧 拼接Packet
        RTMPPacket *packet = createVideoPackage(buf, len, tms, live);
        ret = sendPacket(packet);
    }

    return ret;
}

RTMPPacket *createVideoPackage(int8_t *buf, int len, const long tms, Live *live) {
    buf += 4;
    len -= 4;

    RTMPPacket *packet = (RTMPPacket *) malloc(sizeof(RTMPPacket));
    int body_size = 9 + len;
    RTMPPacket_Alloc(packet, body_size);
    if (buf[0] == 0x65) {
        packet->m_body[0] = 0x17;
    } else {
        packet->m_body[0] = 0x27;
    }
    packet->m_body[1] = 0x01;
    packet->m_body[2] = 0x00;
    packet->m_body[3] = 0x00;
    packet->m_body[4] = 0x00;
    //长度
    packet->m_body[5] = (len >> 24) & 0xff;
    packet->m_body[6] = (len >> 16) & 0xff;
    packet->m_body[7] = (len >> 8) & 0xff;
    packet->m_body[8] = (len) & 0xff;
    //数据
    memcpy(&packet->m_body[9], buf, len);

    //拼接成功       配置 直播怎么做到 配置
    packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
    packet->m_nBodySize = body_size;
    packet->m_nChannel = 0x04;
    packet->m_hasAbsTimestamp = 0;
    //系统赋值
    packet->m_nTimeStamp = tms;
    //数据包大小  给服务器看的
    packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
    packet->m_nInfoField2 = live->rtmp->m_stream_id;
    return packet;
}

int sendPacket(RTMPPacket *pPacket) {
    //发送数据包
    int r = RTMP_SendPacket(live->rtmp, pPacket, 1);
    RTMPPacket_Free(pPacket);
    free(pPacket);
    return r;
}

RTMPPacket *createVideoPackage(Live *live) {
    RTMPPacket *packet = (RTMPPacket *) malloc(sizeof(RTMPPacket));
    //了解rtmp的结构  就知道下面的写法  当作固定算法即可
    //每个包的大小固定          记录一下16是如何计算出来的
    int body_size = 16 + live->sps_len + live->pps_len;
    //实例化数据包
    RTMPPacket_Alloc(packet, body_size);
    int i = 0;
    packet->m_body[i++] = 0x17;
    packet->m_body[i++] = 0x00;
    //CompositionTime
    packet->m_body[i++] = 0x00;
    packet->m_body[i++] = 0x00;
    packet->m_body[i++] = 0x00;
    //AVC sequence header
    packet->m_body[i++] = 0x01;

    packet->m_body[i++] = live->sps[1]; //profile 如baseline、main、 high
    packet->m_body[i++] = live->sps[2]; //profile_compatibility 兼容性
    packet->m_body[i++] = live->sps[3]; //  level
    //固定写法
    packet->m_body[i++] = 0xFF;
    packet->m_body[i++] = 0xE1;
    //sps 长度  2个字节
    //live->sps_len  ===、2个字节
    packet->m_body[i++] = (live->sps_len >> 8) & 0xFF;  //高8位
    packet->m_body[i++] = live->sps_len & 0xFF;         //低8位
    memcpy(&packet->m_body[i], live->sps, live->sps_len);
    //拷贝 i++后面去 方便后面继续赋值
    i += live->sps_len;
    packet->m_body[i++] = 0x01;

    //pps length
    packet->m_body[i++] = (live->pps_len >> 8) & 0xff;
    packet->m_body[i++] = live->pps_len & 0xff;
    memcpy(&packet->m_body[i], live->pps, live->pps_len);

    //拼接成功       配置 直播怎么做到 配置
    packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
    packet->m_nBodySize = body_size;
    packet->m_nChannel = 0x04;//这里不一定要写0x04
    packet->m_hasAbsTimestamp = 0;
    //设置为0，表示系统赋值
    packet->m_nTimeStamp = 0;
    //数据包是大还是小  给服务器看的
    packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
    packet->m_nInfoField2 = live->rtmp->m_stream_id;
    return packet;
}

void prepareVideo(int8_t *buf, int len, Live *live) {
    for (int i = 0; i < len; i++) {
        if (buf[i] == 0x00 && buf[i + 1] == 0x00
            && buf[i + 2] == 0x00
            && buf[i + 3] == 0x01 && buf[i + 4] == 0x68) {
            //计算sps长度
            live->sps_len = i - 4;
            //根据长度   初始化容器
            live->sps = static_cast<int8_t *>(malloc(live->sps_len));
            //copy sps内存到容器
            memcpy(live->sps, buf + 4, live->sps_len);

            //PPS
            live->pps_len = len - i - 4;
            live->pps = static_cast<int8_t *>(malloc(live->pps_len));
            memcpy(live->pps, buf + i + 4, live->pps_len);
            break;
        }
    }
}
